#include "flash.h"

static uint8_t _g_sectorBuffer[FLASH_SECTOR_SIZE] = {0}; // 扇区缓冲

// 数据(发送/接收)
static uint8_t _flash_recv(void);
static void _flash_send(const uint8_t ch);
// 状态(读取/写入)
static uint8_t _flash_readStatus(void);
static void _flash_writeStatus(const uint8_t status);
// 写入(使能/禁用)
static void _flash_writeEnable(void);
static void _flash_writeDisable(void);

// 等待就绪
#define WaitBusy() \
    while (_flash_readStatus() & 0x01) {}

/**
 * @name 接收数据
 * @description
 * @function
 * @returns {uint8_t} 数据
 * @requires []
 */
static uint8_t _flash_recv(void) {
    // 启用时钟信号
    while (SPI_GetFlagStatus(FLASH_SPI, SPI_FLAG_TXE) != SET) {}
    SPI_SendData(FLASH_SPI, 0xff);
    // 接收数据信息
    while (SPI_GetFlagStatus(FLASH_SPI, SPI_FLAG_RXNE) != SET) {}
    return SPI_ReceiveData(FLASH_SPI);
}

/**
 * @name 发送数据
 * @description
 * @function
 * @param {uint8_t} ch 数据
 * @returns {void}
 * @requires [ch]
 */
static void _flash_send(const uint8_t ch) {
    // 发送数据信息
    while (SPI_GetFlagStatus(FLASH_SPI, SPI_FLAG_TXE) != SET) {}
    SPI_SendData(FLASH_SPI, ch);
    // 接收无意数据
    while (SPI_GetFlagStatus(FLASH_SPI, SPI_FLAG_RXNE) != SET) {}
    SPI_ReceiveData(FLASH_SPI);
}

/**
 * @name 读取状态
 * @description
 * @function
 * @returns {uint8_t} 状态
 * @requires []
 */
static uint8_t _flash_readStatus(void) {
    uint8_t status;
    // 使能片选
    FLASH_CS_Clr();
    // 发送命令
    _flash_send(0x05);
    // 接收数据
    status = _flash_recv();
    // 禁用片选
    FLASH_CS_Set();
    return status;
}

/**
 * @name 写入状态
 * @description
 * @function
 * @param {uint8_t} status 状态(0x00|0x01)
 * @returns {void}
 * @requires [status]
 */
static void _flash_writeStatus(const uint8_t status) {
    // 使能片选
    FLASH_CS_Clr();
    // 发送命令
    _flash_send(0x01);
    // 发送数据
    _flash_send(status);
    // 禁用片选
    FLASH_CS_Set();
}

/**
 * @name 写使能
 * @description
 * @function5
 * @returns {void}
 * @requires []
 */
static void _flash_writeEnable(void) {
    // 使能片选
    FLASH_CS_Clr();
    // 发送命令
    _flash_send(0x06);
    // 禁用片选
    FLASH_CS_Set();
}

/**
 * @name 写禁用
 * @description
 * @function
 * @returns {void}
 * @requires []
 */
static void _flash_writeDisable(void) {
    // 使能片选
    FLASH_CS_Clr();
    // 发送命令
    _flash_send(0x04);
    // 禁用片选
    FLASH_CS_Set();
}

/**
 * @name 擦除扇区
 * @description
 * @function
 * @param {uint16_t} sector 扇区(0~FLASH_END_ADDR/FLASH_SECTOR_SIZE)
 * @returns {void}
 * @requires [sector]
 */
static void flash_eraseSector(const uint16_t sector) {
    uint32_t addr = sector * FLASH_SECTOR_SIZE;
    // 写入使能
    _flash_writeEnable();
    // 等待就绪
    WaitBusy();
    // 使能片选
    FLASH_CS_Clr();
    // 擦除扇区
    _flash_send(0x20);
    _flash_send(addr >> 16 & 0xff);
    _flash_send(addr >> 8 & 0xff);
    _flash_send(addr & 0xff);
    // 禁用片选
    FLASH_CS_Set();
    // 等待就绪
    WaitBusy();
    // 写入禁用
    _flash_writeDisable();
}

/**
 * @name 写入页(仅写非擦)
 * @description
 * @function
 * @param {uint8_t} *buffer 写入数据
 * @param {uint32_t} addr 写入地址(FLASH_START_ADDR~FLASH_END_ADDR)
 * @param {uint16_t} size 写入大小(0~FLASH_PAGE_SIZE)
 * @returns {void}
 * @requires [buffer,addr,size]
 */
static void flash_writePage(const uint8_t *buffer, const uint32_t addr, const uint16_t size) {
    uint32_t cur;
    // 写入使能
    _flash_writeEnable();
    // 等待就绪
    WaitBusy();
    // 使能片选
    FLASH_CS_Clr();
    // 发送命令
    _flash_send(0x02);
    _flash_send(addr >> 16 & 0xff);
    _flash_send(addr >> 8 & 0xff);
    _flash_send(addr & 0xff);
    // 发送数据
    for (cur = 0; cur < size; ++cur)
        _flash_send(buffer[cur]);
    // 禁用片选
    FLASH_CS_Set();
    // 等待就绪
    WaitBusy();
    // 写入禁用
    _flash_writeDisable();
}

/**
 * @name 任意写入(仅写非擦)
 * @description
 * @function
 * @param {uint8_t} *buffer 写入数据
 * @param {uint32_t} address 写入地址(FLASH_START_ADDR~FLASH_END_ADDR)
 * @param {uint16_t} size 写入大小(0~FLASH_SIZE)
 * @returns {void}
 * @requires [buffer,address,size]
 */
static void flash_writeArbitrarily(const uint8_t *buffer, const uint32_t address, const uint32_t size) {
    uint8_t *buf = (uint8_t *)buffer;
    uint32_t addr = (uint32_t)address, length = (uint32_t)size;
    uint16_t pageRemain = FLASH_PAGE_SIZE - (addr % FLASH_PAGE_SIZE);
    // 单页剩余
    if (length < pageRemain)
        pageRemain = length;
    // 循环写入
    while (1) {
        // 写入一页
        flash_writePage(buf, address, pageRemain);
        // 写入完成
        if (length == pageRemain)
            break;
        // 指下一页
        buf += pageRemain;
        addr += pageRemain;
        length -= pageRemain;
        pageRemain = length < FLASH_PAGE_SIZE ? length : FLASH_PAGE_SIZE;
    }
}

/**
 * @name FLASH初始化
 * @description
 * @function
 * @returns {void}
 * @requires []
 */
void flash_init(void) {
    SPI_InitTypeDef SPI_InitStruct;
    GPIO_InitTypeDef GPIO_InitStruct;
    // 时钟使能
    FLASH_RCC_APBPeriphClockCmd;
    // 配置GPIO
    SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
    SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStruct.SPI_CPOL = SPI_CPOL_High;
    SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge;
    SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
    SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStruct.SPI_CRCPolynomial = 43;
    // 初始SPI
    SPI_Init(FLASH_SPI, &SPI_InitStruct);
    // 时钟使能
    FLASH_RCC_AHBPeriphClockCmd;
    // 配置GPIO
    GPIO_InitStruct.GPIO_Pin = FLASH_GPIO_Pin;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
    // 初始GPIO
    GPIO_Init(FLASH_GPIO, &GPIO_InitStruct);
    // 配置复用
    GPIO_PinAFConfig(FLASH_GPIO, FLASH_SPI_SCK, FLASH_GPIO_AF_SPI);
    GPIO_PinAFConfig(FLASH_GPIO, FLASH_SPI_MISO, FLASH_GPIO_AF_SPI);
    GPIO_PinAFConfig(FLASH_GPIO, FLASH_SPI_MOSI, FLASH_GPIO_AF_SPI);
    // 配置片选
    GPIO_InitStruct.GPIO_Pin = FLASH_CS;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
    // 初始片选
    GPIO_Init(FLASH_GPIO, &GPIO_InitStruct);
    // 使能SPI
    SPI_Cmd(FLASH_SPI, ENABLE);
}

/**
 * @name 获取Id
 * @description
 * @function
 * @returns {uint16_t} Id
 * @requires []
 */
uint16_t flash_getId(void) {
    uint16_t id;
    // 使能片选
    FLASH_CS_Clr();
    // 发送命令
    _flash_send(0X90);
    _flash_send(0x00);
    _flash_send(0x00);
    _flash_send(0x00);
    // 接收数据
    id = _flash_recv() << 8 | _flash_recv();
    // 禁止片选
    FLASH_CS_Set();
    // 返回数据
    return id;
}

/**
 * @name 清空整片
 * @description
 * @function
 * @returns {void}
 * @requires []
 */
void flash_erase(void) {
    uint16_t i;
    // 清空整片
    for (i = 0; i < FLASH_END_ADDR / FLASH_SECTOR_SIZE; ++i)
        flash_eraseSector(i);
}

/**
 * @name 读取数据
 * @description
 * @function
 * @param {uint8_t} *buffer 数据
 * @param {uint32_t} address 地址
 * @param {uint32_t} size 大小
 * @returns {void}
 * @requires [buffer,address,size]
 */
void flash_read(uint8_t *buffer, const uint32_t address, const uint32_t size) {
    uint16_t i;
    // 地址非法
    if (address + size > FLASH_END_ADDR)
        return;
    // 使能片选
    FLASH_CS_Clr();
    // 发送命令
    _flash_send(0X03);
    _flash_send(address >> 16 & 0xff);
    _flash_send(address >> 8 & 0xff);
    _flash_send(address & 0xff);
    // 接收数据
    for (i = 0; i < size; ++i)
        buffer[i] = _flash_recv();
    // 禁止片选
    FLASH_CS_Set();
}

/**
 * @name 写入数据
 * @description
 * @function
 * @param {uint8_t} *buffer 数据
 * @param {uint32_t} address 地址
 * @param {uint32_t} size 大小
 * @returns {void}
 * @requires [buffer,address,size]
 */
void flash_write(const uint8_t *buffer, const uint32_t address, const uint32_t size) {
    uint16_t i, secpos, secoff, secremain;
    uint32_t addr = (uint32_t)address, length = (uint32_t)size;
    uint8_t *buf = (uint8_t *)buffer, *sectorBuffer = _g_sectorBuffer;
    // 地址非法
    if (address + size > FLASH_END_ADDR)
        return;
    // 计算扇区
    secpos = addr / FLASH_SECTOR_SIZE;
    // 扇区偏移
    secoff = addr % FLASH_SECTOR_SIZE;
    // 扇区剩余
    secremain = FLASH_SECTOR_SIZE - secoff;
    // 循环写入
    if (length <= secremain)
        secremain = length;
    while (1) {
        // 读取扇区
        flash_read(sectorBuffer, secpos * FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE);
        // 擦除标志
        for (i = 0; i < secremain; ++i)
            if (sectorBuffer[secoff + i])
                break;
        // 无需擦除
        if (i >= secremain)
            flash_writeArbitrarily(buf, addr, secremain);
        else {
            // 擦除扇区
            flash_eraseSector(secpos);
            // 复制数据
            for (i = 0; i < secremain; ++i)
                sectorBuffer[i + secoff] = buf[i];
            // 写入扇区
            flash_writeArbitrarily(sectorBuffer, secpos * FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE);
        }
        // 写入结束
        if (length == secremain)
            break;
        // 指下一扇
        ++secpos;
        secoff = 0x0;
        buf += secremain;
        addr += secremain;
        length -= secremain;
        secremain = length < FLASH_SECTOR_SIZE ? length : FLASH_SECTOR_SIZE;
    }
}
